﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Transactions;
using IBS.Bll.KnowledgeBase;
using IBS.Bll.Tools;
using IBS.Model;
using IBS.Model.Base;
using IBS.Dal;
using IBS.ServiceContracts;
using log4net;
using System.Web.Security;

namespace IBS.Bll.Authority
{

    public class AuthorityService : ServiceBase, IAuthorityService
    {

        #region Fields

        private ILog log = LogManager.GetLogger(typeof(AuthorityService));
        private IAdmissionService admissionService;
        private readonly IKnowledgeBaseService _knowledgeBaseService;
        private RequestValidator requestValidator;

        private UserValidator userValidator;
        private CodeCardValidator codeCardValidator;
        #endregion

        #region Constructors

        public AuthorityService(IIbsContext context, IAdmissionService admissionService, IKnowledgeBaseService knowledgeBaseService)
            : base(context)
        {
            this.admissionService = admissionService;
            _knowledgeBaseService = knowledgeBaseService;
            requestValidator = new RequestValidator(context, admissionService);
            codeCardValidator = new CodeCardValidator(context);
            userValidator = new UserValidator(context);
        }

        #endregion

        #region Members

        #region Requests

        public void DeleteRegRequest(int requestId)
        {
            try
            {
                log.InfoFormat("DeleteRegRequest id ={0} is starting ...", requestId);
                //Validation
                var request = GetRegRequestById(requestId);
                _context.Requests.DeleteObject(request);
                _context.SaveChanges();
                log.InfoFormat("Request has been deleted");
            }

            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
        }

        public void SubmitRegRequest(Request regRequest)
        {
            try
            {
                log.InfoFormat("SubmitRegReqest is starting ...");

                //Validation
                if (requestValidator.RequestExists(regRequest))
                    throw new BusinessException("Same user already exists");

                _context.Requests.AddObject(regRequest);
                log.InfoFormat("Save reg request");
                _context.SaveChanges();
                log.InfoFormat("Request id = {0} has been submited is start ...", regRequest.Id);
            }

            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
        }

        public void AproveRegRequest(int requestId, string login)
        {
            try
            {
                log.InfoFormat("AproveRegRequest is staring ...");
                var request = GetRegRequestById(requestId);
                request.Status = (int)RequestStatus.Approved;
                _context.SaveChanges();
                log.InfoFormat("Request id = {0} has been approved by login = {1}", request.Id);
                NotificationService.SendNotificationEmail(
                    request.Email,
                    request.LastName + " " + request.FirstName,
                    "IBanking запрос на подключение интернет банкинга",
                    String.Format("Ваш запрос был успешно обработан. Номер запроса #{0}", request.Id)
                    );
            }
            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
        }

        public void RejectRegRequest(int requestId, string login)
        {
            try
            {
                log.InfoFormat("RejectRegRequest is staring ...");
                var request = GetRegRequestById(requestId);

                request.Status = (int)RequestStatus.Rejected;
                _context.SaveChanges();
                log.InfoFormat("Request id = {0} has been rejected by login = {1}", request.Id);
                NotificationService.SendNotificationEmail(
                    request.Email,
                    request.LastName + " " + request.FirstName,
                    "IBanking запрос на подключение интернет банкинга",
                    String.Format("Ваш запрос был отклонен. Номер запроса #{0}", request.Id)
                    );
            }
            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
        }

        public IList<Request> GetRegRequests(Func<Request, bool> predicate = null, int? page = null)
        {
            IList<Request> requests;
            try
            {
                log.InfoFormat("GetRegRequests is starting ...");
                requests = _context.Requests.GetByCriteria(page, predicate);
                log.InfoFormat("Requests have been got");

            }
            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
            return requests;
        }

        public Request GetRegRequestById(int id)
        {
            try
            {
                return GetRegRequests(x => x.Id == id).Single();

            }
            catch (InvalidOperationException)
            {
                throw new BusinessException("Request does not exist");
            }
            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
        }

        #endregion

        #region CodeCard Maintenance


        public int GetCurrentCodeCardNumber(string userName)
        {

            var codeCard = _context.CodeCards
              .FirstOrDefault(c => c.UserName == userName);
            if (codeCard == null)
            {
                throw new BusinessException("User have to be matched with CodeCard");
            }
            var r = new Random(DateTime.Now.Millisecond);
            codeCard.CurrentPasswordNumber = r.Next(1, 5);

            _context.SaveChanges();
            return codeCard.CurrentPasswordNumber;
        }

        public bool TryCodeCard(string userName, string value)
        {
            var codeCard = _context.CodeCards
                .FirstOrDefault(c => c.UserName == userName);

            if (codeCard == null)
            {
                throw new BusinessException("User have to be matched with CodeCard");
            }
            var currentPassword =
                codeCard.GetType().GetProperty("C" + codeCard.CurrentPasswordNumber).GetValue(codeCard, null);
            return currentPassword.Equals(value);
        }

        public IList<CodeCard> GetCodeCards(Func<CodeCard, bool> predicate = null, int? page = null)
        {
            try
            {
                log.InfoFormat("GetCodeCards is starting ...");

                var requests = _context.CodeCards.GetByCriteria(page, predicate);
                log.InfoFormat("Requests have been got");
                return requests;
            }

            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
        }

        public IList<CodeCard> GetMutchedCodeCards(int? page = null)
        {
            return GetCodeCards(c => !String.IsNullOrEmpty(c.UserName), page);
        }

        public IList<CodeCard> GetUnmatchedCodeCards(int? page = null)
        {
            return GetCodeCards(c => String.IsNullOrEmpty(c.UserName), page);
        }

        #endregion

        #region Users

        public Client GetClientByUserName(string userName)
        {
            try
            {
                var client = _context.ClientUsers.Single(c_u => c_u.UserName == userName).Client;
                return client;
            }
            catch (InvalidOperationException)
            {
                throw new BusinessException("Client does not exist");
            }
        }


        public MembershipUser CreateUser(int clientId, CodeCard codeCard, string userName, string password, string email)
        {
            MembershipUser result;
            try
            {
                using (var scope = new TransactionScope())
                {
                    //Validation
                    var client = _knowledgeBaseService.GetClientById(clientId);

                    if (!codeCardValidator.CodeCardCanBeMatched(codeCard))
                    {
                        throw new BusinessException("Code card can't be matched. It already mutched or doesn't exist");
                    }
                    codeCard.UserName = userName;

                    log.InfoFormat("Creating new user name = {0} email = {1} ...", userName, email);
                    result = admissionService.CreateUser(userName, password, email);
                    if (result == null)
                    {
                        throw new BusinessException("Can't create user");
                    }
                    admissionService.AddUserToRole(userName, "user");
                    var clientUser = new Client_User()
                                         {
                                             ClientId = client.Id,
                                             UserName = userName
                                         };
                    
                    _context.ClientUsers.AddObject(clientUser);
                    _context.SaveChanges();
                    scope.Complete();
                    log.InfoFormat("Success");
                }
            }

            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw;
            }
            return result;
        }

        public MembershipUser CreateOperator(string userName, CodeCard codeCard, string password, string email)
        {
            MembershipUser result;
            try
            {
                using (var scope = new TransactionScope())
                {
                    log.InfoFormat("Creating new operator name = {0} email = {1} ...", userName, email);
                    if (!codeCardValidator.CodeCardCanBeMatched(codeCard))
                    {
                        throw new BusinessException("Code card can't be matched. It already mutched or doesn't exist");
                    }
                    codeCard.UserName = userName;

                    result = admissionService.CreateUser(userName, password, email);
                    if (result == null)
                    {
                        throw new BusinessException("Can't create operator");
                    }
                    admissionService.AddUserToRole(userName, "operator");
                    _context.SaveChanges();
                    scope.Complete();
                    log.InfoFormat("Success");
                }
            }
            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw new BusinessException("Can't create operator");
            }
            return result;
        }

        public MembershipUser CreateAdmin(string userName, CodeCard codeCard, string password, string email)
        {
            MembershipUser result;
            try
            {
                using (var scope = new TransactionScope())
                {
                    log.InfoFormat("Creating new admin name = {0} email = {1} ...", userName, email);

                    if (!codeCardValidator.CodeCardCanBeMatched(codeCard))
                    {
                        throw new BusinessException("Code card can't be matched. It already mutched or doesn't exist");
                    }
                    codeCard.UserName = userName;

                    result = admissionService.CreateUser(userName, password, email);
                    if (result == null)
                    {
                        throw new BusinessException("Can't create admin");
                    }
                    admissionService.AddUserToRole(userName, "admin");
                    _context.SaveChanges();
                    scope.Complete();
                    log.InfoFormat("Success");
                }
            }
            catch (Exception exception)
            {
                log.Error(exception.Message, exception);
                throw new BusinessException("Can't create admin");
            }
            return result;
        }

        #endregion


        #endregion

        public string GetClentUserName(Client client)
        {
            try
            {
                return client.Client_User.FirstOrDefault().UserName;
            }
            catch (Exception)
            {
                throw new BusinessException("Client or user does not exist");
            }
        }
    }
}
